﻿#ifndef _PpuCore_h_
#define _PpuCore_h_

#include <stdint.h>
#include "PpuRegisters.h"
#include "PpuPalette.h"
#define _in_
#define _out_
#define _in_out_

#define SET_BIT        1
#define CLR_BIT        0

#define PPU_SPRITES_PALETTE_SIZE            16        // Размер палитры спрайтов (в байтах )
#define PPU_FONE_PALETTE_SIZE               16        // Размер палитры фона (в байтах )

#define PPU_PALETTE_FONE_BASE_ADDR          0x3F00    // Начальный адресс палитры фона
#define PPU_PALETTE_SPRITES_BASE_ADDR       0x3F10    // Начальный адресс палитры спрайтов

#define PPU_SCREEN_PAGE1_BASE_ADDR          0x2000    // Начальный адрес первой экранной страници 
#define PPU_SCREEN_PAGE2_BASE_ADDR          0x2400    // Начальный адрес второй экранной страници
#define PPU_SCREEN_PAGE3_BASE_ADDR          0x2800    // Начальный адрес третьей экранной страници
#define PPU_SCREEN_PAGE4_BASE_ADDR          0x2C00    // Начальный адрес четвертой экранной страници


#define PPU_SCREEN_PAGE_SIZE                0x0400    

#define PPU_CHR0_ROM_BASE_ADDR              0x0000    // Начальный адрес знакогенератора 0
#define PPU_CHR1_ROM_BASE_ADDR              0x1000    // Начальный адрес знакогенератора 1


#define PPU_SCREEN_PAGE_SYMBOLS_AREA_SIZE   960       // Размер области символов в экранной странице


#define PPU_HORIZONTAL_RESOLUTION           256       // Горизонтальное разрешение экрана ( для системы PAL )
#define PPU_VERTICAL_PAL_RESOLUTION         240       // Вертикальное разрешение экрана ( для системы PAL )
//224


#define PPU_VISIBLE_SCAN_LINE_LENGTH        256       // Длина видимой области скан-линии ( в пикселях )
#define PPU_NON_VISIBLE_SCAN_LINE_LENGTH    85        // Длина невидимой области скан-линии ( в пикселях )
#define PPU_PRE_RENDERING_SCAN_LINES        1         // Количество скан линий между ??? и следующим кадром
#define PPU_VISIBLE_SCAN_LINES              240       // Количество скан видимых скан - линий

#define PPU_PAL_POST_RENDERING_SCAN_LINES   4         // Количество скан-линий после окончания прорисовки картинки до формирования NMI
#define PPU_PAL_SCAN_LINES_AFTER_NMI        20        // Количество скан-линий после формирования NMI до момента сброса флага NMI



#define PPU_ROM_SYMBOL_HEIGHT               8         // Высота символа из знакогенератора (в пикселях)
#define PPU_ROM_SYMBOL_WIDTH                8         // Ширина символа из знакогенератора (в пикселях)
#define PPU_ROM_SYMBOL_SIZE                 16        // Размер символа из знакогенератора в памяти (в байтах)
#define PPU_NUMBER_OF_SYMBOLS_IN_LINE       32        // Максимальное количество знакомест в строке  
//#define PPU_SCREEN_PAGE_SIZE                1024

#define PPU_SPRITES_MEMORY_SIZE             256       // Размер памяти спрайтов
#define PPU_NUMBER_OF_SPRITES               64
#define PPU_MAX_NUMBER_OF_SPRITES_ON_LINE   8


struct SpriteItems_t
{
    uint8_t color            : 2;
    uint8_t notUsed          : 3;
    uint8_t spriteUpperFone  : 1;
    uint8_t horizontalMirror : 1;
    uint8_t verticalMirror   : 1;
};

struct SpriteInfo_t
{
    uint8_t          yPos;
    uint8_t          chrIdx;
    SpriteItems_t    item;
    uint8_t          xPos;
};

enum VideoEncodingSystem_t
{
    NTSC_VIDEO_SYSTEM = 0,
    PAL_VIDEO_SYSTEM  = 1
};

typedef uint8_t (*PpuBusReadCallBack_t)      ( void* context, uint16_t busAddr );
typedef void    (*PpuBusWriteCallBack_t)     ( void* context, uint16_t busAddr, uint8_t busData );
typedef void    (*PpuPresentFrameCallBack_t) ( void* context, uint8_t* pData, uint16_t len, uint16_t posInFrame );
typedef void    (*CpuExecuteCallBack_t)      ( void* context, uint32_t cycles );
typedef void    (*PpuInterruptCallBack_t)    ( void* context );


class Ppu_t
{
    public:
        void SetBusWriteCallBack( PpuBusWriteCallBack_t busWriteCallBack, void * pContext );
        void SetBusReadCallBack( PpuBusReadCallBack_t busReadCallBack, void * pContext );
        void SetPresentFrameCallBack( PpuPresentFrameCallBack_t presentFrameCallBack, void * pContext );
        void SetVsyncSignalCallBack( PpuInterruptCallBack_t vsyncCallBack, void * pContext );
        void SetLineCounterCallBack( PpuInterruptCallBack_t lineCounterCallBack, void * pContext );
        void SetCpuExecuteCallBack( CpuExecuteCallBack_t cpuExecuteCallBack, void * pContext );


    public:
        // -------------- Initialization ----------------        
        //void SetContextPointer( _in_ void * context );
        void SetVideoSystem( VideoEncodingSystem_t videoSystem );
        //-----------------------------------------------
        
        //  ------ For Save Games ---------------
        void SavePpuContext( uint8_t* pOutData );
        void LoadPpuContext( uint8_t* pInData );
        // --------------------------------------

        void Reset(void);
        void RenderFrame( void );
        
            // --- Интерфейс взаимодействия с процессором ---
        uint8_t Read(uint16_t addr);
        void Write(uint16_t addr, uint8_t data);
        // ----------------------------------------------


    private:
        PpuBusWriteCallBack_t     fp_PpuBusWriteCallBack;
        PpuBusReadCallBack_t      fp_PpuBusReadCallBack;            
        PpuPresentFrameCallBack_t fp_PresentFrameCallBack;
        PpuInterruptCallBack_t    fp_VsyncSignalCallBack;
        CpuExecuteCallBack_t      fp_CpuExecuteCallBack;
        PpuInterruptCallBack_t    fp_IrqHookCallBack;


    private:
        void RenderFoneScanline( uint16_t scanline );
        void RenderSpritesScanline(uint16_t scanline);
        void ReflectPalette( void );
        bool IsNmiBlank( void );
        void RenderScanLine( uint16_t scanLine );


    private:
        PpuRegisters_t m_PpuRegisters;
        uint16_t       m_ActiveVideoRamAddr;
    private:
        
        uint8_t        m_FoneScanLine[ PPU_HORIZONTAL_RESOLUTION ];
        uint8_t        m_SpriteTopScanLine[ PPU_HORIZONTAL_RESOLUTION ];
        uint8_t        m_SpriteBottomScanLine[ PPU_HORIZONTAL_RESOLUTION ];
        uint8_t        m_ScanLine[ PPU_HORIZONTAL_RESOLUTION ];


        uint8_t        m_PaletteFone   [ PPU_FONE_PALETTE_SIZE ];
        uint8_t        m_PaletteSprites[ PPU_SPRITES_PALETTE_SIZE ];
        SpriteInfo_t   m_SpritesMemory [ PPU_NUMBER_OF_SPRITES ];
        SpriteInfo_t   m_SpritesBuff   [ PPU_MAX_NUMBER_OF_SPRITES_ON_LINE ];
        

        uint16_t       m_FoneChrBaseAddr;      // Базовый адрес знакогенератора для формирования фона
        uint16_t       m_SpritesChrBaseAddr; // Базовый адрес знакогенератора для формирования спрайтов
        //bool           m_NewAddrWasSet;
        bool           m_VideoRamAddrRegHighPart;
        bool           m_VerticalScrollingValue;
        uint8_t        m_AddrIncValue;
        uint8_t        m_SpriteSize;
        
        uint8_t        m_SpriteLineLow;
        uint8_t        m_SpriteLineHigh;

        uint8_t        m_SymbolLineLow;
        uint8_t        m_SymbolLineHigh;
        uint8_t        m_SymbolItemByte;
        uint8_t        m_LastWritten;

        void*          m_pContext;
};

#endif